﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Common.Tools
{
    /// <summary>
    /// 实体比较类
    /// </summary>
    public class CompareModelHelper
    {
        /// <summary>
        /// 比较两个Model修改了什么属性值
        /// </summary>
        /// <param name="beforeModel"></param>
        /// <param name="afterModel"></param>
        /// <param name="fieldsDescriptionDic"></param>
        /// <param name="listTypeKeyFieldNameDic">List类型的Key键的Name值字典</param>
        /// <returns></returns>
        public static string Compare(object beforeModel, object afterModel, Dictionary<string, string> fieldsDescriptionDic, Dictionary<string, string> listTypeKeyFieldNameDic)
        {
            var resultBuilder = new StringBuilder();
            try
            {
                //反射遍历
                //获得实例对象公共属性
                PropertyInfo[] fields = beforeModel.GetType().GetProperties();
                //遍历字段
                foreach (var field in fields)
                {
                    if (!field.CanRead) continue;
                    //取得字段的值
                    object beforeValue = field.GetValue(beforeModel, null);
                    object afterValue = field.GetValue(afterModel, null);

                    //都为空不比较
                    if (beforeValue == null && afterValue == null) continue;

                    if (field.PropertyType.IsValueType || field.PropertyType.Name.StartsWith("String"))
                    {
                        #region 1.值类型,或者String类型处理
                        var fieldDesc = GetValueByName(fieldsDescriptionDic, field.Name);
                        //无字段描述就直接跳过
                        if (string.IsNullOrWhiteSpace(fieldDesc)) continue;

                        //防止ToString发生异常
                        if (beforeValue == null) beforeValue = "“”";
                        if (afterValue == null) afterValue = "“”";

                        //直接比较是否相等
                        if (beforeValue.ToString() != afterValue.ToString())
                        {
                            resultBuilder.AppendFormat("{0}：由：{1}，修改为：{2}；<br />", fieldDesc, beforeValue, afterValue);
                        }
                        #endregion
                    }
                    else if (field.PropertyType.IsGenericType)
                    {
                        #region 2.List类型处理
                        var objectDesc = GetValueByName(fieldsDescriptionDic, field.Name);
                        //无字段描述就直接跳过
                        if (string.IsNullOrWhiteSpace(objectDesc)) continue;

                        //判断两个对象是否一致，若一致说明未修改，若不一致说明有修改
                        //修改有三种情况：新增，修改，删除，通过两次遍历找出

                        var beforeValueArray = beforeValue as IEnumerable<object>;
                        var afterValueArray = afterValue as IEnumerable<object>;
                        if (beforeValueArray == null || afterValueArray == null) continue;

                        var beforeFieldDescDic = GetObjectArrayDescription(beforeValueArray, field.Name, listTypeKeyFieldNameDic, fieldsDescriptionDic);
                        var afterFieldDescDic = GetObjectArrayDescription(afterValueArray, field.Name, listTypeKeyFieldNameDic, fieldsDescriptionDic);

                        //新数据与原数据比较
                        var afterKeyindex = 0;
                        foreach (var key in afterFieldDescDic.Keys)
                        {
                            var beforeKeyIndex = ContainsKeyIndex(beforeFieldDescDic, key);
                            if (beforeKeyIndex > -1)
                            {
                                //原数据中存在表示修改
                                var beforeValueList = beforeValueArray.ToList();
                                var afterValueList = afterValueArray.ToList();
                                var objectFieldDesc = Compare(beforeValueList[beforeKeyIndex], afterValueList[afterKeyindex], fieldsDescriptionDic, listTypeKeyFieldNameDic);
                                if (string.IsNullOrWhiteSpace(objectFieldDesc)) objectFieldDesc = "“”";
                                resultBuilder.AppendFormat("修改了{0}:<br />{1}", objectDesc, objectFieldDesc);
                            }
                            else
                            {
                                //原数据中不存在表示添加
                                resultBuilder.AppendFormat("添加了{0}:{1}<br />", objectDesc, afterFieldDescDic[key]);
                            }
                            afterKeyindex++;
                        }

                        //原数据与新数据比较
                        foreach (var key in beforeFieldDescDic.Keys)
                        {
                            if (!afterFieldDescDic.ContainsKey(key))
                            {
                                //新数据中不存在表示删除
                                resultBuilder.AppendFormat("删除了{0}:{1}<br />", objectDesc, beforeFieldDescDic[key]);
                            }
                        }

                        #endregion
                    }
                    else
                    {
                        #region 3.引用类型处理
                        if (beforeValue != null && afterValue != null)
                        {
                            //递归
                            resultBuilder.Append(Compare(beforeValue, afterValue, fieldsDescriptionDic, listTypeKeyFieldNameDic));
                        }
                        #endregion
                    }
                }
            }
            catch (Exception ex)
            {
                return string.Empty;
            }
            return resultBuilder.ToString();
        }

        /// <summary>
        /// 字典中是否存在指定Key,返回索引
        /// </summary>
        /// <param name="dic"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        private static int ContainsKeyIndex(Dictionary<string, string> dic, string key)
        {
            var resultIndex = -1;
            if (dic == null) return resultIndex;
            var index = 0;
            foreach (var item in dic.Keys)
            {
                if (item.Equals(key))
                {
                    resultIndex = index;
                    break;
                }
                index++;
            }
            return resultIndex;
        }

        /// <summary>
        /// 获取集合的对象描述
        /// </summary>
        /// <param name="objectArray"></param>
        /// <param name="listTypeKeyFieldName"></param>
        /// <param name="listTypeKeyFieldNameDic"></param>
        /// <param name="fieldsDescriptionDic"></param>
        /// <returns></returns>
        private static Dictionary<string, string> GetObjectArrayDescription(IEnumerable<object> objectArray, string listTypeKeyFieldName, Dictionary<string, string> listTypeKeyFieldNameDic, Dictionary<string, string> fieldsDescriptionDic)
        {
            var resultDic = new Dictionary<string, string>();
            var keyFieldName = GetValueByName(listTypeKeyFieldNameDic, listTypeKeyFieldName);
            foreach (var obj in objectArray)
            {
                var objProperties = obj.GetType().GetProperties();
                if (objProperties == null) continue;

                var index = 0;
                var keyFieldValue = string.Empty;
                StringBuilder fieldDescBuilder = new StringBuilder();
                foreach (var field in objProperties)
                {
                    if (!field.CanRead) continue;
                    //没有描述不处理
                    var fieldDesc = GetValueByName(fieldsDescriptionDic, field.Name);
                    if (string.IsNullOrWhiteSpace(fieldDesc)) continue;

                    if (field.PropertyType.IsValueType || field.PropertyType.Name.StartsWith("String"))
                    {
                        //值类型,或者String类型处理

                        //取得字段的值 
                        object fieldValue = field.GetValue(obj, null);
                        //防止ToString发生异常
                        if (fieldValue == null) fieldValue = "“”";

                        if (index == 0 || keyFieldName == field.Name)
                        {
                            keyFieldValue = fieldValue.ToString();
                        }
                        fieldDescBuilder.AppendFormat("{0}={1}，", fieldDesc, fieldValue);
                    }
                    index++;
                }
                var objDesc = string.Empty;
                if (fieldDescBuilder.Length > 0)
                    objDesc = string.Format("{0}；", fieldDescBuilder.ToString().TrimEnd('，'));

                if (resultDic.ContainsKey(keyFieldValue)) continue;
                resultDic.Add(keyFieldValue, objDesc);
            }
            return resultDic;
        }

        /// <summary>
        /// 获取字典的Value
        /// </summary>
        /// <param name="dic"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        private static string GetValueByName(Dictionary<string, string> dic, string name)
        {
            if (dic == null) return string.Empty;
            return dic.ContainsKey(name) ? dic[name] : string.Empty;
        }

        public static Dictionary<string, string> ListTypeDictionary
        {
            get
            {
                return new Dictionary<string, string>()
                {
                    {"ProductLabels", "LabelId"},
                    {"ProductScenerys", "SceneryId"},
                    //{"ProductPriceNames", ""},

                };
            }
        }

        public static Dictionary<string, string> DicProductModify
        {
            get
            {
                return new Dictionary<string, string>()
                {
                    {"ProductLabels", "产品标签"},
                    {"ProductScenerys", "产品景点"},
                   {"LabelName", "标签名称"},
                    {"SceneryName", "景点名称"},
                       {"LastModTime", "最后修改时间"},

                };
            }
        }

        #region 表对应注释

        public static Dictionary<string, string> DicCustomerInfo
        {
            get
            {
                return new Dictionary<string, string>()
                {
                    {"Id", "出游人主键ID"},
                    {"TrueName", "游客真实姓名"},
                    {"CardType", "游客证件类型(I:身份证 P:护照)"},
                    {"CardNo", "游客证件号码"},
                    {"CustomerTel", "游客手机"},
                    {"CustomerBirthday", "游客生日"},
                    {"CustomerSex", "游客性别(M 先生 F 女士)"}
                };
            }
        }

        #endregion
    }
}
